package org.school.streaming.net.client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.util.StringTokenizer;

import javax.swing.Timer;

import org.school.streaming.net.RequestType;
import org.school.streaming.net.State;
import org.school.streaming.net.client.listeners.timerListener;

/**
 * The Client class usage: java Client [ Server hostname ] [ Server listening
 * port ] [ Video file requested ]
 * 
 * @authors Jeffrey Goudzwaard, Davin Terrizzi
 * @version 1.0
 * @date 18-4-2013
 */

public class Client {
	// A new line, for lazy people ;)
	private static final String CRLF = "\r\n";

	private static Client INSTANCE;

	// The reader
	private static BufferedReader READER;

	// The writer
	private static BufferedWriter WRITER;

	// The name of the video file
	private String videoFileName;

	// The port for receiving packets
	private int port = 25000;

	private String host;

	// The payload type for MJPEG video
	private int mJPEGType = 26;

	// UDP packet
	private DatagramPacket dgp;

	// Socket for sending/receiving UDP packets
	private DatagramSocket ds;

	// The buffer to store the data that we received from the server
	private byte[] buffer;

	// The current state
	private State state;

	// The socket to send/receive messages
	private Socket socket;

	// The sequence number of the messages within the session
	private int seqnr = 0;

	// The id of the session ( given by the server )
	private int id = 0;

	// The timer for receiving data
	private Timer timer;

	public static Client getClient() {
		if (INSTANCE == null) {
			INSTANCE = new Client();
		}

		return INSTANCE;
	}

	/**
	 * Constructor
	 */
	private Client() {
		init();
	}

	private void init() {
		// Initializing the timer
		timer = new Timer(20, new timerListener());
		timer.setInitialDelay(0);
		timer.setCoalesce(true);

		// Allocate enough memory for the buffer
		buffer = new byte[15000];
	}
	
	/**
	 * Send specific type of request to server via BufferedWriter
	 * Flush afterwards
	 * @param requestType RequestType to send to server
	 */
	public void sendRequest(RequestType requestType) {
		try {
			switch (requestType) {
			case SETUP:
				WRITER.write("SETUP " + videoFileName + " RTSP/1.0" + CRLF);
				WRITER.write("CSeq: " + seqnr + CRLF);
				WRITER.write("Transport: RTP/UDP; client_port= " + port + CRLF);
				break;
			case PAUSE:
				WRITER.write("PAUSE " + videoFileName + " RTSP/1.0" + CRLF);
				WRITER.write("CSeq: " + seqnr + CRLF);
				WRITER.write("Session: " + id + CRLF);
				break;
			case PLAY:
				WRITER.write("PLAY " + videoFileName + " RTSP/1.0" + CRLF);
				WRITER.write("CSeq: " + seqnr + CRLF);
				WRITER.write("Session: " + id + CRLF);
				break;
			case TEARDOWN:
				WRITER.write("TEARDOWN " + videoFileName + " RTSP/1.0" + CRLF);
				WRITER.write("CSeq: " + seqnr + CRLF);
				WRITER.write("Session: " + id + CRLF);
				break;
			case NULL:
				break;
			default:
				break;
			}
		} catch (IOException e) {
			System.err.println(e);
		} finally {
			try {
				WRITER.flush();
			} catch (IOException e) {
				System.err.println(e);
			}
		}
	}

	/**
	 * Parse incoming response from server and reply accordingly
	 * @return Integer representing response code sent by server
	 */
	public int parseServerResponse() {
		int replyCode = 0;

		try {
			
			// Read  full response from BufferedReader
			String stateLine = READER.readLine();
			System.out.println(stateLine);

			// Find response code and set to integer
			StringTokenizer tokens = new StringTokenizer(stateLine);
			tokens.nextToken();

			replyCode = Integer.parseInt(tokens.nextToken());

			// If response code is not 200, stop immediately
			if (replyCode == 200) {
				// Log current frame no.
				String seqNumLine = READER.readLine();
				System.out.println(seqNumLine);

				// Log current session no.
				String sessionLine = READER.readLine();
				System.out.println(sessionLine);

				// Set current session id
				tokens = new StringTokenizer(sessionLine);
				tokens.nextToken();
				id = Integer.parseInt(tokens.nextToken());
			}
		} catch (Exception e) {
			System.err.println(e);
			System.exit(1);
		}

		return replyCode;
	}

	public Socket getSocket() {
		return socket;
	}

	public void setSocket(Socket socket) {
		this.socket = socket;
	}

	public BufferedReader getReader() {
		return READER;
	}

	public void setReader(BufferedReader reader) {
		READER = reader;
	}

	public BufferedWriter getWriter() {
		return WRITER;
	}

	public void setWriter(BufferedWriter writer) {
		WRITER = writer;
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public int getSeqNr() {
		return seqnr;
	}

	public void setSeqnr(int seqnr) {
		this.seqnr = seqnr;
	}

	public Timer getTimer() {
		return timer;
	}

	public DatagramPacket getDatagramPacket() {
		return dgp;
	}

	public void setDatagramPacket(DatagramPacket dgp) {
		this.dgp = dgp;
	}

	public byte[] getBuffer() {
		return buffer;
	}

	public void setBuffer(byte[] buffer) {
		this.buffer = buffer;
	}

	public DatagramSocket getDatagramSocket() {
		return ds;
	}

	public void setDatagramSocket(DatagramSocket ds) {
		this.ds = ds;
	}

	public String getVideoFileName() {
		return videoFileName;
	}

	public void setVideoFileName(String videoFileName) {
		this.videoFileName = videoFileName;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public int getMJPEGType() {
		return mJPEGType;
	}

	public void setMJPEGType(int mJPEGType) {
		this.mJPEGType = mJPEGType;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}


}